/**
 * @file  start.S
 *
 * @brief Kernel startup routine, and SWI and IRQ setup routines
 *
 * @author Nora Tarano <ntarano@andrew.cmu.edu>
 * @author Yuan Meng <yuanm@andrew.cmu.edu>
 * @author Yipeng Yun <yipengy@andrew.cmu.edu>
 * @date Nov 17, 2011
 *
 * @author Mike Kasick <mkasick@andrew.cmu.edu>
 * @date   Tue, 23 Oct 2007 11:20:33 -0400
 *
 * @author Kartik Subramanian <ksubrama@andrew.cmu.edu>
 * @date   2008-07-06
 */

#include <asm.h>

#define CPSR_IRQ_FLAG  0x80
#define CPSR_FIQ_FLAG  0x40
#define CPSR_USR_MODE  0x10

#define LOAD_ADDR 0xa0000000
#define USR_STACK 0xa3000000

#define SWI_IMM_MASK 0x00ffffff

	.file   "start.S"

FUNC(_start)
	/* Branch immediately to main without altering the execution environment.
	 * This indirection is needed to guarantee that the program entry point is
	 * LOAD_ADDR (probably 0xa3000000) without placing strict
	 * requirements on the layout of kernel.c.  But first, provide main with the
	 * jump table address as one of its arguments. */
	mov     r2, r8
	b       kmain

	.text
	
@ Call the SWI dispatcher with the SWI number and return to user mode.
FUNC(swi_handler)
	stmfd	sp!, {r0-r12, lr}

	@ Restore jump table in r8.
	ldr	ip, =global_data
	ldr	r8, [ip]

	@ Determine the SWI number and call the dispatch function.
	ldr	r0, [lr, #-4]
	bic	r0, r0, #~SWI_IMM_MASK
	mov	r1, sp
	bl	swi_dispatch

	@ Return to user mode.
	ldmfd	sp!, {r0-r12, pc}^

@ Enter user mode and execute the user program.
FUNC(enter_user_mode)
	@ Stash regs, jump table in r8, and sp for exit_kernel.
	stmfd	sp!, {r4-r7, r9-r11, lr}
	ldr	ip, =global_data
	str	r8, [ip]
	ldr	ip, =saved_sp
	str	sp, [ip]

	@ Switch to user mode & load user stack.
	msr	cpsr_c, #(CPSR_IRQ_FLAG | CPSR_FIQ_FLAG | CPSR_USR_MODE)
	ldr	sp, =USR_STACK

	@ Push argv NULL terminator.
	mov	ip, $0
	str	ip, [sp, #-4]!

	@ Calculate address of argv[argc].
	add	r1, r1, r0, asl #2
	mov	r2, r0

	@ Push argv pointers from argv[argc-1] .. argv[0].
	copy_loop:
		cmp	r2, $0
		ldrgt	ip, [r1, #-4]!
		strgt	ip, [sp, #-4]!
		subgt	r2, r2, #1
		bgt	copy_loop

		@ Push argc.
		str	r0, [sp, #-4]!

		@ Jump to program.
		ldr	pc, =LOAD_ADDR

@ Exits from the enter_user_mode function by restoring the values
@ saved on the stack there.
FUNC(exit_kernel)
	@ Restore regs & enter_user_mode sp (pops IRQ stack junk).
	ldr	ip, =saved_sp
	ldr	sp, [ip]
	@ldr	ip, =icmr_init	@ restore icmr
	@str	sp, [ip]
	@ldr	ip, =iclr_init	@ restore iclr
	@str	sp, [ip]
	ldmfd	sp!, {r4-r7, r9-r11, pc}

@ Sets up the IRQ stack, at addr 0xA1000000
FUNC(setup_irq_stack)
    msr	CPSR_c, #0xD2	@ switch to IRQ
	mov	sp, #0xa1000000	@ setup IRQ stack
	msr	CPSR_c, #0x53	@ switch to SVC
	mov	pc, lr			@ return

@ Enables IRQs by unmasking the IRQ cpsr flag
FUNC(unmask_irq)
	mrs r0, cpsr			@ store cpsr
	bic r0, #CPSR_IRQ_FLAG	@ unmask irq
	msr cpsr, r0			@ reload new cpsr
	mov pc, lr				@ return

@ Disables IRQs by masking the IRQ cpsr flag
FUNC(mask_irq)
	mrs r0, cpsr
	orr r0, #CPSR_IRQ_FLAG
	msr cpsr, r0
	mov pc, lr

FUNC(irq_handler)
	stmfd	sp!, {r0-r12, lr}
	@ Restore jump table in r8
	ldr	ip, =global_data
	ldr	r8, [ip]
	bl	c_irq_handler		@ Call C IRQ handler
	ldmfd sp!, {r0-r12, lr}	@ restore gp registers
  	subs  pc, lr, #4		@ return from handler to user mode

	.comm	saved_sp, 4, 4
